#include "quizwindow.h"
#include "ui_quizwindow.h"

QuizWindow::QuizWindow(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::QuizWindow)
{
    ui->setupUi(this);
    currentQuestion = 0;
    ui->questionBodyBrowser->setOpenExternalLinks(true);
    clearUI();
    connect(ui->openAnswerEdit,SIGNAL(returnPressed()),this,SLOT(on_btnCheck_clicked()));

    // set the default style sheets
    correctAnswerStylesheet = "background: #008B00; color: white";
    correctUnselectedAnswerStylesheet = "background: #A2CD5A; color: black";
    badAnswerStylesheet = "background: maroon; color: white";

    // shortcuts
    ui->btnNextQuestion->setShortcut(QKeySequence(Qt::Key_Tab));
    ui->btnCheck->setShortcut(QKeySequence(" "));
}

QuizWindow::~QuizWindow()
{
    delete ui;
}

/*!
  Function that reads quiz from file and displays a random
  question.
  @param filename Path to file to open.
  @param score Initial score for all of the questions, ignored if
    there's session information in quiz file and ignoreSession is
    set to false.
  @param penalty Penalty for wrong answers, ignored if
    there's session information in quiz file and ignoreSession is
    set to false.
  @param ignoreSession If set to True application will ignore session
    information in quiz file.
*/
void QuizWindow::openQuiz(QString filename = "file.txt",
                          int score = 5,
                          int penalty = 5,
                          bool ignoreSession = false) {
    QFile plik(filename);
    quiz.loadFromFile(&plik,score,penalty,ignoreSession);
    this->initialAnswersN = quiz.questions.size();
    ui->progressBar->setMaximum(quiz.questions.size());
    displayRandomQuestion();
}

/*!
  This function overrides the default Qwidget::show method, so it first
  displays a FtestyWelcome window, loads a quiz and then shows itself
  with a random question from an opened test.
*/
void QuizWindow::show() {
    if (!currentQuestion) {
        FtestyWelcome welcome(this);
        if (welcome.exec() == QDialog::Accepted) {
            openQuiz(welcome.fileName,welcome.initScore,welcome.penalty,welcome.ignoreSession);
        } else {
            QApplication::quit();
            return;
        }
    }
    displayRandomQuestion();
    QWidget::show();
}

/*!
  This function updates stats in the applications, which are
  displayed by progress bar and it's tooltip.
*/
void QuizWindow::updateStats() {
    int progress = this->initialAnswersN - quiz.questions.size();
    ui->progressBar->setValue(progress);
    ui->progressBar->setToolTip(
            QString(tr("Pozostało %1 pytań\nObecne pytanie będzie powtórzone jeszcze %2 razy.\nW razie błędu ilość powtórzeń obecnego pytania wzrośnie o %3.")
                    .arg(quiz.questions.size())
                    .arg(this->currentQuestion->score)
                    .arg(this->currentQuestion->penalty)));
}

/*!
  Function that clears the UI before next question is displayed.
*/
void QuizWindow::clearUI() {
    answerRadios.clear();
    ui->answersListWidget->clear();
    if (!selectedAnswers)
        delete[] selectedAnswers;
    selectedAnswers = 0;
    ui->openAnswerEdit->setVisible(false);
    ui->openAnswerEdit->clear();
    ui->openAnswerEdit->setStyleSheet("");      // reset stylesheet to default
    ui->openAnswerHintBtn->setVisible(false);
    ui->answersListWidget->setVisible(false);
    ui->btnCheck->setEnabled(true);
}

/*!
  Function that automatically displays a question stored in
  QuizWindow::currentQuestion . Check for the type of question,
  stores it in QuizWindow::questionType and runs
  QuizWindow::displayCurrentQuestionOne() or
  QuizWindow::displayCurrentQuestionMany() depending in the type.
*/
void QuizWindow::displayCurrentQuestion() {
    selectedAnswers = new bool[this->currentQuestion->answers.count()];
    // if there's only one answer, then we assume that's an open question.
    if (currentQuestion->answers.count() == 1) {
        displayCurrentQuestionOpen();
        this->questionType = OPEN_ANSWER;
        return;
    }
    // if not, that's a quiz question!
    if (currentQuestion->ntAnswers > 1) {
        displayCurrentQuestionMany();
        this->questionType = MULTI_ANSWER;
    } else {
        displayCurrentQuestionOne();
        this->questionType = SINGLE_ANSWER;
    }
}

/*!
  Displays current question like a single-correct-answer-type question.
*/
void QuizWindow::displayCurrentQuestionOne() {
    ui->questionBodyBrowser->setText(currentQuestion->question.trimmed());
    ui->answersListWidget->setVisible(true);
    for (int i=0;i<currentQuestion->answers.size();i++) {
        QRadioButton* radio = new QRadioButton(currentQuestion->answers[i],0);
        radio->setShortcut(QKeySequence(Qt::Key_1));
        QListWidgetItem* item = new QListWidgetItem(ui->answersListWidget);
        answerRadios.insert(radio, i);
        connect(radio,SIGNAL(clicked(bool)),this,SLOT(answerSelected(bool)));
        ui->answersListWidget->setItemWidget(item,radio);
        selectedAnswers[i] = false;
    }
}

/*!
  Displays current question like a multiple choices type question.
*/
void QuizWindow::displayCurrentQuestionMany() {
    ui->questionBodyBrowser->setText(currentQuestion->question.trimmed());
    for (int i=0;i<currentQuestion->answers.size();i++) {
        QCheckBox* radio = new QCheckBox(currentQuestion->answers[i],0);
        QListWidgetItem* item = new QListWidgetItem(ui->answersListWidget);
        answerRadios.insert(radio, i);
        connect(radio,SIGNAL(clicked(bool)),this,SLOT(answerSelected(bool)));
        ui->answersListWidget->setItemWidget(item,radio);
        selectedAnswers[i] = false;
    }
    ui->answersListWidget->setVisible(true);
}

/*!
  Displays current question like a open question.
*/
void QuizWindow::displayCurrentQuestionOpen() {
    ui->questionBodyBrowser->setText(currentQuestion->question.trimmed());
    ui->openAnswerEdit->setVisible(true);
    ui->openAnswerEdit->setFocus();
}

/*!
  Function that displays a random question.
*/
void QuizWindow::displayRandomQuestion() {
    currentQuestion = quiz.getRandom();

    // We should handle the situation when there are no more questions
    if (!currentQuestion) {
        QMessageBox::information(this,tr("Gratulacje!"),tr("To już wszystkie pytania!"));
        exit(EXIT_SUCCESS);
    }

    clearUI();
    updateStats();
    currentQuestion->shuffleAnswers();
    displayCurrentQuestion();
}

void QuizWindow::answerSelected(bool isTrue) {
    selectAnswer(answerRadios[(QRadioButton*) sender()], isTrue);
}

/*!
  Sets answer as selected in QuizWindow::selectedAnswers.
  @param ansId Selected answer id.
  @param isTrue True if ansId was selected as an answer, false
    if not (for example it was unchecked).
*/
void QuizWindow::selectAnswer(int ansId, bool isTrue) {
    QAbstractButton* btn = answerRadios.key(ansId, 0);
    // let's check if button exists
    if (btn) {
        switch (this->questionType) {
        case SINGLE_ANSWER:
            // if clicked button is not set as checked something's wicked going on
            // so let's just ignore it and try to live like nothing happened.
            if (isTrue) {
                // Only one answer can be selected.
                for (int i=0;i<this->currentQuestion->answers.count();i++) {
                    this->selectedAnswers[i] = (i == ansId);
                }
            }
            break;
        case MULTI_ANSWER:
            // Many answers can be selected, so let's just mark the one there as
            // selected... or not.
            this->selectedAnswers[ansId] = isTrue;
            break;
        default:
            // shouldn't happen ever
            qCritical("QuizWindow::selectAnswer: Unexpected question type detected.");
        }
    }
}

void QuizWindow::on_btnNextQuestion_clicked()
{
    displayRandomQuestion();
}

/*!
  Checks if correct answers are selected in quiz-type question.
  @return true if provided answer is correct
*/
bool QuizWindow::checkQuizAnswer() {
    bool correct = true; // flag set to false if there are bad answers selected
    ui->btnCheck->setEnabled(false);
    for (int i=0;i<this->currentQuestion->answers.count();i++) {
        bool a = currentQuestion->isCorrect(i);
        bool b = selectedAnswers[i];
        if (a && b) {
            // selected and correct? that's good!
            answerRadios.key(i)->setStyleSheet(correctAnswerStylesheet);
        } else if (a) {
            // correct but not selected?
            // well, that's too bad, then.
            answerRadios.key(i)->setStyleSheet(correctUnselectedAnswerStylesheet);
            correct = false;
        } else if (b) {
            // selected but not correct.
            answerRadios.key(i)->setStyleSheet(badAnswerStylesheet);
            correct = false;
        }
    }
    return correct;
}

/*!
  Checks if correct answer is provided in open-type question.
  @return true if provided answer is ok.
*/
bool QuizWindow::checkOpenAnswer() {
    // simplest way possible
    // negation because it returns 0 if strings are the same
    bool correct = !ui->openAnswerEdit->text().trimmed().toLower().
                   compare(currentQuestion->answers[0].trimmed().toLower());
    ui->btnCheck->setEnabled(false);
    if (correct)
        ui->openAnswerEdit->setStyleSheet(correctAnswerStylesheet);
    else {
        ui->openAnswerHintBtn->setVisible(true);
        ui->openAnswerEdit->setStyleSheet(badAnswerStylesheet);
    }
    return correct;
}

void QuizWindow::on_btnCheck_clicked()
{
    bool correct;

    // if btnCheck is not enabled, then we shouldn't check answers
    if (!ui->btnCheck->isEnabled())
        return;

    switch (this->questionType) {
    case OPEN_ANSWER:
        correct = checkOpenAnswer();
        break;
    case SINGLE_ANSWER:
    case MULTI_ANSWER:
        correct = checkQuizAnswer();
        break;
    default:
        qCritical("QuizWindow::on_btnCheck_clicked(): Strange question type encountered.");
    }

    currentQuestion->selectedAnswer(correct);
    if (currentQuestion->score <= 0)
        quiz.removeQuestion(currentQuestion);

    ui->btnNextQuestion->setDefault(true);
}

void QuizWindow::on_btnSave_clicked()
{
    QString fileName = QFileDialog::getSaveFileName(this,
                                 tr("Zapisz obecną sesję"),"",
                                 tr("Plik tekstowy (*.txt);; Wszystkie pliki (*)"));
    if (!fileName.isEmpty()) {
        QFile file (fileName);
        quiz.saveToFile(&file);
    }
}

void QuizWindow::on_btnSettings_clicked()
{
    SettingsDialog sd;
    if (sd.exec() == QDialog::Accepted) {
        for (int i=0;i<quiz.questions.size();i++) {
            if (sd.setScore) {
                ((Question*) quiz.questions[i])->setScore(sd.score);
            }
            if (sd.setPenalty) {
                ((Question*) quiz.questions[i])->setPenalty(sd.penalty);
            }
        }
    }
    updateStats();
}

void QuizWindow::on_btnHelp_clicked()
{
    AboutDialog a;
    a.exec();
}

/*!
  Clicking this button shows the correct answer to the user.
  It should be possible only with open questions.
*/
void QuizWindow::on_openAnswerHintBtn_clicked()
{
    ui->openAnswerEdit->setText(currentQuestion->answers[0]);
    ui->openAnswerEdit->setStyleSheet(correctUnselectedAnswerStylesheet);
}
